package de.tempel.fittslaw;

import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JOptionPane;

import processing.core.PApplet;
import de.tempel.fittslaw.objects.CoordinateSystem;
import de.tempel.fittslaw.objects.Experiment;
import de.tempel.fittslaw.objects.Target;
import de.tempel.fittslaw.objects.Trial;
import de.tempel.fittslaw.regression.Line;

public class FittsLawTester extends PApplet {

	private static final long serialVersionUID = 1L;

	private static final int WINDOW_WIDTH = 1000;
	private static final int WINDOW_HEIGHT = 400;

	private Target leftTarget;
	private Target rightTarget;

	private Boolean trialIsRunning = false;
	private Boolean experimentIsRunning = true;

	private Experiment experiments;

	private List<Line> regressionLines = new ArrayList<Line>();
	private List<List<Point2D>> pointsForLine = new ArrayList<List<Point2D>>();

	/**
	 * Standardkonstruktor
	 */
	public FittsLawTester() {
		setupObjects();
	}

	/**
	 * Setzt Objekte zusammen
	 */
	private void setupObjects() {

		// Trials zusammenbauen
		String currentInputDevice = askForInputDevice();
		experiments = new Experiment(currentInputDevice);
		Trial firstTrial = experiments.getCurrentTrial();

		// Ziele auf Position des ersten Trials positionieren
		leftTarget = getLeftTargetForTrial(firstTrial);
		rightTarget = getRightTargetForTrial(firstTrial);

	}

	private String askForInputDevice() {
		return JOptionPane
				.showInputDialog("Welches Eingabeger�t verwenden Sie?");
	}

	@Override
	public void setup() {
		size(WINDOW_WIDTH, WINDOW_HEIGHT);
	}

	@Override
	public void draw() {

		if (!experimentIsRunning) {

			CoordinateSystem cs = new CoordinateSystem(new Point2D.Float(0, 0),
					WINDOW_WIDTH, WINDOW_HEIGHT, 0.5f, 0.5f, 5f, 2f,
					regressionLines, pointsForLine);
			cs.draw(this);

		} else {

			Trial currentTrial = experiments.getCurrentTrial();

			// clear
			fill(0);
			this.rect(0, 0, width, height);
			fill(255);

			leftTarget.draw(this);
			rightTarget.draw(this);

			if (trialIsRunning) {

				// Verstrichene Zeite
				fill(255);
				this.text("Time: " + currentTrial.runningTime(), 20,
						WINDOW_HEIGHT - 20);
				this.text("Hit: " + currentTrial.getClickCount(), 100,
						WINDOW_HEIGHT - 20);
				this.text("Missed: " + currentTrial.getMissedCount(), 150,
						WINDOW_HEIGHT - 20);

				// Pruefe ob Trial abgelaufen ist
				if (currentTrial.isFinished()) {

					System.out.println("Trial Finished!");
					System.out
							.println("\t Results: " + currentTrial.toString());

					// Pr�fe ob es noch Trials gibt
					if (!experiments.hasNext()) {

						System.out.println("Experiment Finished!");
						trialIsRunning = false;
						experimentIsRunning = false;

						Line currentRegressionLine = experiments
								.calculateRegressionLine();
						List<Point2D> currentIdMtPoints = experiments
								.getIdMtPoints();

						regressionLines.add(currentRegressionLine);
						pointsForLine.add(currentIdMtPoints);

						reset();

						System.out.println("\t y = "
								+ currentRegressionLine.getA() + " + "
								+ currentRegressionLine.getB() + " * x");

						System.out.println("\t Index of Performance: "
								+ experiments.calculateIndexOfPerformance());
						System.out
								.println("\t Index of Performance ignoring a: "
										+ experiments
												.calculateIndexOfPerformanceIgnoringA());

						System.out
								.println("---------------------------------------------------");

						writeExperimentResultInFile(experiments);
						createHTMLResultTable(experiments);

					} else {

						stopCurrentTrial();
						Trial nextTrial = experiments.next();
						setPaddles(nextTrial);

					}

				}

			} else {
				this.text("Klick Target to Start next Trial", 20,
						WINDOW_HEIGHT - 20);
			}
		}
	}

	private void setPaddles(Trial trial) {
		leftTarget = getLeftTargetForTrial(trial);
		rightTarget = getRightTargetForTrial(trial);
	}

	private void reset() {
		leftTarget.reset();
		rightTarget.reset();
	}

	private void writeExperimentResultInFile(Experiment exp) {

		List<Trial> tr = exp.getTrials();

		File outputFile = new File("result.txt");

		try {

			if (!outputFile.exists()) {
				outputFile.createNewFile();
			}

			FileWriter fw = new FileWriter(outputFile, true);
			PrintWriter pr = new PrintWriter(fw);

			pr.println("------------------------------------------------------");
			pr.println("Experiment with " + tr.size() + " Trials and '"
					+ exp.getInputDevice() + "' as Input device:");

			int i = 1;
			for (Trial t : tr) {
				pr.println("\t " + i + ": " + t.toString());
				i++;
			}

			Line regressionLine = exp.calculateRegressionLine();

			pr.println("\t Resulting Regression Line: y = "
					+ regressionLine.getA() + " + " + regressionLine.getB()
					+ " * x");

			pr.println("\t Index of Performance: "
					+ experiments.calculateIndexOfPerformance());
			pr.println("\t Index of Performance ignoring a: "
					+ experiments.calculateIndexOfPerformanceIgnoringA());

			pr.println("------------------------------------------------------");

			// Cleanup
			fw.flush();
			pr.flush();

			fw.close();
			pr.close();

		} catch (Exception e) {
			System.out.println("Error writing Outputfile!");
			e.printStackTrace();
		}

	}

	private void createHTMLResultTable(Experiment exp) {

		List<Trial> trials = exp.getTrials();

		File outputFile = new File("table.html");

		try {

			if (!outputFile.exists()) {
				outputFile.createNewFile();
			}

			FileWriter fw = new FileWriter(outputFile, true);
			PrintWriter pr = new PrintWriter(fw);

			pr.print("<h3>");
			pr.println("Experiment with '" + exp.getInputDevice()
					+ "' aus input device");
			pr.print("</h3>");
			pr.println("<table border='1'>");
			pr.println("<tr style='background-color:#AEAEAE'>");
			pr.println("<td>");
			pr.println("Trial");
			pr.println("</td>");
			pr.println("<td>");
			pr.println("D");
			pr.println("</td>");
			pr.println("<td>");
			pr.println("W");
			pr.println("</td>");
			pr.println("<td>");
			pr.println("ID");
			pr.println("</td>");
			;
			pr.println("<td>");
			pr.println("Hits");
			pr.println("</td>");
			pr.println("<td>");
			pr.println("Missed");
			pr.println("</td>");
			pr.println("<td>");
			pr.println("MT");
			pr.println("</td>");
			pr.println("</tr>");

			int i = 1;
			for (Trial t : trials) {

				pr.println("<tr>");
				pr.println("<td>");
				pr.println(i);
				pr.println("</td>");
				pr.println("<td>");
				pr.println(t.getDistanceBetweenTargets());
				pr.println("</td>");
				pr.println("<td>");
				pr.println(t.getWidthOfTargets());
				pr.println("</td>");
				pr.println("<td>");
				pr.println(t.getIndexOfDifficulty());
				pr.println("</td>");
				pr.println("<td>");
				pr.println(t.getClickCount());
				pr.println("</td>");
				pr.println("<td>");
				pr.println(t.getMissedCount());
				pr.println("</td>");
				pr.println("<td>");
				pr.println(t.getMovementTime());
				pr.println("</td>");
				pr.println("</tr>");

				i++;
			}

			pr.println("</table>");
			Line regressionLine = exp.calculateRegressionLine();
			pr.println("Resulting Regression Line: y = "
					+ regressionLine.getA() + " + " + regressionLine.getB()
					+ " * x");
			pr.println("<br/>");
			pr.println("Index of Performance: "
					+ experiments.calculateIndexOfPerformance());
			pr.println("<br/>");
			pr.println("Index of Performance ignoring a: "
					+ experiments.calculateIndexOfPerformanceIgnoringA());
			pr.println("<br/>");

			// Cleanup
			fw.flush();
			pr.flush();

			fw.close();
			pr.close();

		} catch (Exception e) {
			System.out.println("Error writing Outputfile!");
			e.printStackTrace();
		}
	}

	@Override
	public void mouseClicked(MouseEvent me) {

		if (!experimentIsRunning) {
			experimentIsRunning = true;
			String newInputDevice = askForInputDevice();
			experiments.reset(newInputDevice);
			Trial nextTrial = experiments.getCurrentTrial();
			setPaddles(nextTrial);
		}

		Trial currentTrial = experiments.getCurrentTrial();

		Point klickPoint = me.getPoint();

		if (leftTarget.isClickable() && leftTarget.touches(klickPoint)) {
			if (!trialIsRunning) {
				System.out.println("Starting Trial: "
						+ experiments.getCurrentTrialId());
				startNextTrial();
			}

			leftTarget.setClickable(false);
			rightTarget.setClickable(true);

			currentTrial.targetHit();

		} else if (rightTarget.isClickable() && rightTarget.touches(klickPoint)) {
			if (!trialIsRunning) {
				System.out.println("Starting Trial: "
						+ experiments.getCurrentTrialId());
				startNextTrial();
			}

			rightTarget.setClickable(false);
			leftTarget.setClickable(true);

			currentTrial.targetHit();

		} else {
			// System.out.println("\t --- Daneben!");
			currentTrial.targetMissed();
		}

	}

	private void startNextTrial() {
		Trial currentTrial = experiments.getCurrentTrial();
		currentTrial.start();
		trialIsRunning = true;
	}

	private void stopCurrentTrial() {
		trialIsRunning = false;
	}

	private Target getRightTargetForTrial(Trial currentTrial2) {
		Trial currentTrial = experiments.getCurrentTrial();
		return new Target(currentTrial.getxPositionOfLeftTarget()
				+ currentTrial.getWidthOfTargets()
				+ currentTrial.getDistanceBetweenTargets(),
				currentTrial.getWidthOfTargets(), WINDOW_HEIGHT);
	}

	private Target getLeftTargetForTrial(Trial currentTrial) {
		return new Target(currentTrial.getxPositionOfLeftTarget(),
				currentTrial.getWidthOfTargets(), WINDOW_HEIGHT);
	}

	public static void main(String[] args) {
		PApplet.main(new String[] { "de.tempel.fittslaw.FittsLawTester" });
	}

}